
使用printOn()打印堆栈内容，不如为堆栈实现<{}<操作符。然而，通常<{}<操作符会实现为非成员函数，然后内联调用printOn():

\begin{lstlisting}[style=styleCXX]
template<typename T>
class Stack {
	...
	void printOn(std::ostream& strm) const {
		...
	}
	friend std::ostream& operator<< (std::ostream& strm,
	Stack<T> const& s) {
		s.printOn(strm);
		return strm;
	}
};
\end{lstlisting}

这意味着用于类Stack<>的<{}<操作符不是一个函数模板，而是在需要时用类模板实例化的“普通”函数。

\begin{tcolorbox}[colback=webgreen!5!white,colframe=webgreen!75!black]
\hspace*{0.75cm}其是一个模板实体，参见12.1节。
\end{tcolorbox}

但当试图声明友元函数并实现时，事情会变得更加复杂。这里有两种选择:

\begin{enumerate}
\item 
隐式声明一个新的函数模板，但要使用不同的模板参数，比如U:

\begin{lstlisting}[style=styleCXX]
template<typename T>
class Stack {
	...
	template<typename U>
	friend std::ostream& operator<< (std::ostream&, Stack<U> const&);
};
\end{lstlisting}

再次使用T或跳过模板参数都不起作用(要么内部的T隐藏外部的T，要么在命名空间作用域中声明一个非模板函数)。

\item 
可以将Stack<T>的输出操作符转发声明为模板，这样就需要转发声明Stack<T>:

\begin{lstlisting}[style=styleCXX]
template<typename T>
class Stack;
template<typename T>
std::ostream& operator<< (std::ostream&, Stack<T> const&);
\end{lstlisting}

然后，将该函数声明为友元:

\begin{lstlisting}[style=styleCXX]
template<typename T>
class Stack {
	...
	friend std::ostream& operator<< <T> (std::ostream&,
	Stack<T> const&);
};
\end{lstlisting}

注意“函数名”操作符<{}<后面的<T>。因此，需要将非成员函数模板的特化声明为友元。若没有<T>，需要声明新的非模板函数。详细信息请参见12.5.2。
\end{enumerate}

其他情况下，可以对没有定义<{}<操作符的元素使用这个类。只有对这个堆栈调用操作符<{}<时才会出现错误:

\begin{lstlisting}[style=styleCXX]
Stack<std::pair<int,int>> ps; // std::pair<> has no operator<< defined
ps.push({4, 5}); // OK
ps.push({6, 7}); // OK
std::cout << ps.top().first << '\n'; // OK
std::cout << ps.top().second << '\n'; // OK
std::cout << ps << '\n'; // ERROR: operator<< not supported
\end{lstlisting}




